//
//  GCDTableViewController.m
//  objective_c_language
//
//  Created by 王玉龙 on 2018/6/5.
//  Copyright © 2018年 longlong. All rights reserved.
//
#define CellIdentifer @"UITableViewCell"

#import "GCDTableViewController.h"
#import "QueueTaskViewController.h"

@interface GCDTableViewController ()
@property (nonatomic ,strong) NSArray<NSString *> *titleArray;
@end

@implementation GCDTableViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(rightBarButtonItemClick)];
    
    // Uncomment the following line to preserve selection between presentations.
    // self.clearsSelectionOnViewWillAppear = NO;
    [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:CellIdentifer];
    // Uncomment the following line to display an Edit button in the navigation bar for this view controller.
    // self.navigationItem.rightBarButtonItem = self.editButtonItem;
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}


- (void)rightBarButtonItemClick
{
    QueueTaskViewController *gcdVC = [[QueueTaskViewController alloc] init];
    [self.navigationController pushViewController:gcdVC animated:YES];
}


#pragma mark - Table view data source

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return self.titleArray.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifer forIndexPath:indexPath];
    cell.textLabel.text = self.titleArray[indexPath.row];
    cell.textLabel.numberOfLines = 0;
    return cell;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    [tableView deselectRowAtIndexPath:indexPath animated:YES];
    
    if (indexPath.row == 0)
    {
        [self syncOrAsyncMethod];
    }
    else if (indexPath.row == 1)
    {
        [self barrier_SyncOrAsyncMethod];
    }
    else if (indexPath.row == 2)
    {
        [self dispatch_group_asyncMethod];
    }
    else if (indexPath.row == 3)
    {
        [self semaphoreTestMethod];//信号量
    }
    else if (indexPath.row == 4)
    {
        [self dispatchGroupMethod];//任务分组
    }
    else if (indexPath.row == 5)
    {
        [self holdThreadCountMethod];//控制线程数量
    }
    else if (indexPath.row == 6)
    {
        [self dispatch_afterMethod];//延迟执行
    }
    else if (indexPath.row == 7)
    {
        [self dispatch_applyMethod];//
    }
    else if (indexPath.row == 8)
    {
        [self mainQueueDeadLockMethod];//主队列造成的死锁
    }
    else if (indexPath.row == 9)
    {
        [self newSerialQueueDeadLockMethod];//串行队列+异步+同步 造成的死锁
    }
    
    
}

#pragma mark - 同步 与 异步

/*
 dispatch_sync : 将指定的Block" 同步"追加到指定的Dispatch Queue中。在追加Block结束之前，dispatch_sync 函数会一直等待。dispatch_sync 函数 block中的代码与 dispatch_sync 函数上下文的代码处于同一线程
 dispatch_async 函数 block中的代码与 dispatch_async 函数上下文的代码处于不同线程
 */
- (void)syncOrAsyncMethod
{
    /*
     dispatch_queue_create(<#const char * _Nullable label#>, <#dispatch_queue_attr_t  _Nullable attr#>)
     该函数的第一个参数指定Serial Dispatch Queue的名称。推荐使用应用程序ID这种逆序全程域名。
     
     第二个参数，当生成Serial Dispatch Queue（串行队列）时，第二个参数设置NULL。
     当生成Concurrent Dispatch Queue(并发队列)，指定为DISPATCH_QUEUE_CONCURRENT
     
     生成的Dispatch Queue必须由程序员负责释放
     */
    
    dispatch_queue_t testQueue = dispatch_queue_create("com.objective_c_language.gcd.test", DISPATCH_QUEUE_CONCURRENT);
    
    NSLog(@"%s ----- 1 ---- %@",__func__,[NSThread currentThread]);
    
    //将指定的Block" 同步"追加到指定的Dispatch Queue中。在追加Block结束之前，dispatch_sync 函数会一直等待。
    // dispatch_sync 函数 block中的代码与 dispatch_sync 函数上下文的代码处于同一线程
    dispatch_sync(testQueue, ^{
        NSLog(@"%s ----- 2 ---- %@",__func__,[NSThread currentThread]);
        sleep(5);
    });
    NSLog(@"%s ----- 3 ---- %@",__func__,[NSThread currentThread]);
    
    // dispatch_async 函数 block中的代码与 dispatch_async 函数上下文的代码处于不同线程
    dispatch_async(testQueue, ^{
        NSLog(@"%s ----- 4 ---- %@",__func__,[NSThread currentThread]);
    });
    NSLog(@"%s ----- 5 ---- %@",__func__,[NSThread currentThread]);
    //    dispatch_release(testQueue);
    
    //dispatch_sync在主线程中调用的话就会发生死锁。
    //    dispatch_sync(dispatch_get_main_queue(), ^{
    //        NSLog(@"%s ----- 6",__func__);
    //    });
    //    NSLog(@"%s ----- 7",__func__);
    
    
}

#pragma mark - 同步栅栏 与 异步栅栏

/*
 dispatch_barrier_sync 与 dispatch_barrier_async
 相同点：都是将 dispatch_queue_t 一分为2，前面的任务执行完，才执行 自己 block 中的任务
 不同点：
 （1）、dispatch_barrier_sync 与 dispatch_sync 一样，将指定的Block同步追加到指定的Dispatch Queue中，
 dispatch_barrier_sync 函数 block中的代码与 dispatch_barrier_sync 函数上下文的代码处于同一线程
 （2）、dispatch_barrier_async 与 dispatch_async 一样，
 dispatch_barrier_sync 函数 block中的代码与 dispatch_barrier_sync 函数上下文的代码处于不同线程
 */
- (void)barrier_SyncOrAsyncMethod
{
    NSLog(@" ----- %s ---- ",__func__);
    
    dispatch_queue_t testQueue = dispatch_queue_create("com.objective_c_language.gcd.test2", DISPATCH_QUEUE_CONCURRENT);
    
    NSLog(@"1 ----- %@",[NSThread currentThread]);
    
    
    dispatch_sync(testQueue, ^{
        NSLog(@"2 ----- %@",[NSThread currentThread]);
        sleep(2);
    });
    NSLog(@"3 ----- %@",[NSThread currentThread]);
    
    dispatch_async(testQueue, ^{
        NSLog(@"4 ----- %@",[NSThread currentThread]);
        sleep(2);
    });
    NSLog(@"5 ----- %@",[NSThread currentThread]);
    
    
    dispatch_barrier_sync(testQueue, ^{
        NSLog(@"6 ----- %@",[NSThread currentThread]);
        sleep(2);
    });
    NSLog(@"7 ----- %@",[NSThread currentThread]);
    
    
    dispatch_sync(testQueue, ^{
        NSLog(@"8 ----- %@",[NSThread currentThread]);
        sleep(2);
    });
    NSLog(@"9 ----- %@",[NSThread currentThread]);
    
    dispatch_async(testQueue, ^{
        NSLog(@"10 ----- %@",[NSThread currentThread]);
        sleep(2);
    });
    NSLog(@"11 ----- %@",[NSThread currentThread]);
    
    
    dispatch_barrier_async(testQueue, ^{
        NSLog(@"12 ----- %@",[NSThread currentThread]);
        sleep(2);
    });
    NSLog(@"13 ----- %@",[NSThread currentThread]);
    
    dispatch_sync(testQueue, ^{
        NSLog(@"14 ----- %@",[NSThread currentThread]);
        sleep(2);
    });
    NSLog(@"15 ----- %@",[NSThread currentThread]);
    
    dispatch_async(testQueue, ^{
        NSLog(@"16 ----- %@",[NSThread currentThread]);
        sleep(2);
    });
    NSLog(@"17 ----- %@",[NSThread currentThread]);
    
}


#pragma mark - 任务分组
/*
 GCD 的 dispatch_group_t 功能可以将多个任务分组，等待分组里面的所有任务执行完成之后，GCD 的 dispatch_group_notify 方法可以通知。通常会配合一些常见的场景来考察，比如同时上传 10 张图片，全部上传完成后通知用户。
 
 
 */
- (void)dispatchGroupMethod
{
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t testQueue = dispatch_queue_create("com.objective_c_language.gcd.test6", DISPATCH_QUEUE_CONCURRENT);
    for (int i = 0; i < 10; i ++)
    {
        dispatch_group_async(group, testQueue, ^{
            NSLog(@"执行第 %d 次任务 == %@",i,NSThread.currentThread);
            [NSThread sleepForTimeInterval:10];//处理耗时任务
        });
    }
    //将所有的任务都加入 group ，等待所有的任务执行完成后，dispatch_group_notify 会被调用。
    dispatch_group_notify(group, testQueue, ^{
        NSLog(@"任务结束 ------ %@",NSThread.currentThread);
    });
    
}

#pragma mark - 同步组 与 异步组

///dispatch_group_async 在dispatch_queue中所有的任务执行完成后
- (void)dispatch_group_asyncMethod
{
    NSLog(@" ----- %s ---- ",__func__);
    
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t testQueue = dispatch_queue_create("com.objective_c_language.gcd.test3", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_group_async(group, testQueue, ^{
        sleep(2);
        NSLog(@"1 ----- %@",[NSThread currentThread]);
    });
    NSLog(@"2 ----- %@",[NSThread currentThread]);
    
    dispatch_group_async(group, testQueue, ^{
        sleep(2);
        NSLog(@"3 ----- %@",[NSThread currentThread]);
    });
    NSLog(@"4 ----- %@",[NSThread currentThread]);
    
    dispatch_group_async(group, testQueue, ^{
        sleep(2);
        NSLog(@"5 ----- %@",[NSThread currentThread]);
    });
    NSLog(@"6 ----- %@",[NSThread currentThread]);
    
    //在dispatch_queue中所有的任务执行完成后 会调用 dispatch_group_notify 中的 block
    dispatch_group_notify(group, testQueue, ^{
        NSLog(@"7 ----- %@",[NSThread currentThread]);
    });
    NSLog(@"8 ----- %@",[NSThread currentThread]);
    
    dispatch_group_async(group, testQueue, ^{
        sleep(2);
        NSLog(@"9 ----- %@",[NSThread currentThread]);
    });
    NSLog(@"10 ----- %@",[NSThread currentThread]);
    
}

#pragma mark - 延迟执行

/*
 如果某一条任务你想等多少时间之后再执行的话，你就完全可以使用这个函数处理，
 需要注意的是：dispatch_after函数并不是在指定时间之后才开始执行处理，而是在指定时间之后将任务追加到主队列中。
 严格来说，这个时间并不是绝对准确的，但想要大致延迟执行任务，dispatch_after函数是很有效的。
 
 */
- (void)dispatch_afterMethod
{
    dispatch_queue_t queue = dispatch_queue_create("dispatch_after", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
        
        NSLog(@"开始执行");
        // DISPATCH_TIME_NOW 当前时间开始
        dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10.0 * NSEC_PER_SEC));
        dispatch_after(time, queue, ^{
            NSLog(@"延迟十秒钟");
        });
        
        NSLog(@"结束执行");
    });
}

/*快速迭代方法：dispatch_apply
 dispatch_apply 类似一个for循环，会在指定的dispatch queue中运行block任务n次，如果队列是并发队列，则会并发执行block任务，
 dispatch_apply是一个同步调用，block任务执行n次后才返回。
 
 通常我们会用 for 循环遍历，但是 GCD 给我们提供了快速迭代的函数dispatch_apply。
 dispatch_apply按照指定的次数将指定的任务追加到指定的队列中，并等待全部队列执行结束。
 
 我们可以利用异步队列同时遍历。比如说遍历 0~5 这6个数字，for 循环的做法是每次取出一个元素，逐个遍历。
 dispatch_apply可以同时遍历多个数字。
 
 由于 dispatch_apply 是同步执行，所以不能在串行队列里执行，否则会造成线程死锁
 */
- (void)dispatch_applyMethod
{
    dispatch_queue_t queue = dispatch_queue_create("dispatch_apply", DISPATCH_QUEUE_CONCURRENT);
    dispatch_apply(6, queue, ^(size_t index) {
        NSLog(@"%zd---%@",index, [NSThread currentThread]);
    });
    
    //因为是在并发队列中异步队执行任务，所以各个任务的执行时间长短不定，最后结束顺序也不定。但是apply---end一定在最后执行。
    //这是因为dispatch_apply函数dispatch_apply是一个同步调用,会等待全部任务执行完毕。
    
    NSLog(@"apply---end");
}

#pragma mark - 信号量
//GCD 不像 NSOperation 那样有直接提供线程数量控制方法，但是通过 GCD 的 semaphore 功能一样可以达到控制线程数量的效果。
/*
 dispatch_semaphore_t dispatch_semaphore_create(long value)//利用给定的输出时创建一个新的可计数的信号量
 
 //如果信号量大于 0 ，信号量减 1 ，执行程序。否则等待信号量
 long dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout);
 
 long dispatch_semaphore_signal(dispatch_semaphore_t dsema);//增加信号量
 */
- (void)semaphoreTestMethod
{
    //并发线程
    dispatch_queue_t concurrentQueue = dispatch_queue_create("com.objective_c_language.gcd.semaphore", DISPATCH_QUEUE_CONCURRENT);
    //串行线程
    dispatch_queue_t serialQueue = dispatch_queue_create("com.objective_c_language.gcd.semaphore2", DISPATCH_QUEUE_SERIAL);
    //创建一个semaphore,并设置最大信号量，最大信号量表示最大线程数量
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(2);
    
    for (int i = 0 ; i < 10 ; i ++)
    {
        dispatch_async(serialQueue, ^{
            // 只有当信号量大于 0 的时候，线程将信号量减 1，程序向下执行
            // 否则线程会阻塞并且一直等待，直到信号量大于 0
            dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
            dispatch_async(concurrentQueue, ^{
                NSLog(@"执行第 %d 次任务 == %@",i,NSThread.currentThread);
                [NSThread sleepForTimeInterval:10];//处理耗时任务
                dispatch_semaphore_signal(semaphore);// 当线程任务执行完成之后，发送一个信号，增加信号量。
            });
        });
    }
    
    NSLog(@"执行任务结束 == %@",NSThread.currentThread);
}

#pragma mark - 控制线程数量

/*
 利用 GCD 的 dispatch_group_t 和 semaphore 功能，我们可以做到控制线程数量，并且在所有任务执行完成之后得到通知。
 */
- (void)holdThreadCountMethod
{
    dispatch_queue_t queue = dispatch_queue_create("com.objective_c_language.gcd.ThreadCount", DISPATCH_QUEUE_CONCURRENT);
    dispatch_group_t group = dispatch_group_create();
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(2);
    for (int i = 0 ; i < 10 ; i ++)
    {
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        
        // 只有当信号量大于 0 的时候，线程将信号量减 1，程序向下执行
        // 否则线程会阻塞并且一直等待，直到信号量大于 0
        
        dispatch_group_async(group, queue, ^{
            NSLog(@"执行第 %d 次任务 == %@",i,NSThread.currentThread);
            [NSThread sleepForTimeInterval:10];//处理耗时任务
            dispatch_semaphore_signal(semaphore);// 当线程任务执行完成之后，发送一个信号，增加信号量。
        });
    }
    
    //既控制了线程数量，也在执行任务完成之后得到了通知。
    dispatch_group_notify(group, queue, ^{
        NSLog(@"任务结束 ------ %@",NSThread.currentThread);
    });
}

#pragma mark - 串行队列死锁

//主队列是串行对列
- (void)mainQueueDeadLockMethod
{
    //主线程+串行队列+同步任务
    //程序报错：Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    dispatch_sync(mainQueue, ^{
        NSLog(@"同步执行 ------- %@",NSThread.currentThread);
    });
    NSLog(@"同步执行完毕");
    
    /*
     开始执行dispatch_sync这个函数，主队列是串行队列，这个函数会把这个任务插入到主队列的最后面（理解队列添加任务）
     主线程执行到这里的时候就会等待插入的这个同步任务执行完之后再执行后面的操作
     但由于这个同步任务是插入到主队列的最后面，最队列前面的任务没有执行完之前是不会执行这个block的(主线程在执行initMainQueue任务)
     这样就造成了一个相互等待的过程，主线程在等待block完返回，block却在等待主线程执行它，这样就造成了死锁，看打印的信息你也就知道block是没有被执行的。
     */
}

- (void)newSerialQueueDeadLockMethod
{
    //串行队列+异步+同步 造成死锁
    dispatch_queue_t queue = dispatch_queue_create("newQueueDeadLockMethod", DISPATCH_QUEUE_SERIAL);
    dispatch_async(queue, ^{
        
        NSLog(@"异步任务A --- %@",NSThread.currentThread);
        
        //程序报错： Thread 4: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
        dispatch_sync(queue, ^{
            NSLog(@"同步任务B --- %@",NSThread.currentThread);
        });
    });
    
    NSLog(@"异步执行之后的代码");
    
    /*
     主线程主队列中执行任务 initDispatchQueue,进入了这个方法，在这个方法里面创建了一个串行队列
     给这个串行队列添加了一个异步任务，由于是异步任务，所以会开启一条新的线程，为了方便描述，我们把新开的这个线程记做线程A, 把这个任务记做任务A，也由于是异步任务，主线程就不会等待这个任务返回，就接着往下执行其他任务了。
     接下来的分析就到了这个线程A上，这个任务A被添加到串行队列之后就开始在线程A上执行，打印出了我们的第一条信息，也证明了不是在主线程,这个也没问题。
     线程A开始执行这个任务A，进入这个任务A之后在这个任务A里面又同步在串行队列里面添加任务，记做任务B，由于任务B是dispatch_sync函数同步添加的，需要立马被执行，就等待线程A执行它
     但是这个任务B是添加到串行队列的末尾的，线程A在没有执行完当前任务A是不会去执行它的，这样就造成线程A在等待当前任务A执行完，任务B又在等待线程A执行它，就形成了死锁
     */
}


#pragma mark - setter and getter

- (NSArray<NSString *> *)titleArray
{
    if (_titleArray == nil)
    {
        _titleArray = @[@"sync 与 async",@"dispatch_barrier_async 与 dispatch_barrier_sync",
                        @"dispatch_group_async",@"semaphore",@"dispatch_group",@"控制线程数量",@"dispatch_after",@"dispatch_apply",@"mainQueue DeadLock",@"newQueue DeadLock"];
    }
    return _titleArray;
}

@end
